package java_thinking.unit_21._21_5;

import java.util.Map;
import java.util.concurrent.*;

/**
 * @Desc:   5-18 基于 FutureTask 的缓存封装 但其中的先检查再执行操作非原子，故仍有缺陷 不要这么做
 *          1.使用 Map<K, Future<V>> 替代之前的 Map<K, V>
 *          2.首先检查某个计算线程是否已经开始(与 Memoizer2 相反，它首先判断某个计算是否启动)
 *          3.如果没有启动那么就创建一个 FutureTask 并注册到 Map 中，然后启动计算；
 *          4.如果已经启动了计算线程那么等待其线程返回结果；
 *
 *          缺陷：复合操作“若没有则添加”是在底层对 Map 对象上执行的，而这个对象无法通过对方法加锁
 *          来确保其原子性；可以使用 ConcurrentHashMap 的原子方法 putIfAbsent
 * @author: cww
 * @DateTime: 2019/6/10 18:56
 */

public class Memoizer3<K, V> implements Computable<K, V> {
private final Map<K, Future<V>> cache = new ConcurrentHashMap<>();

    private final Computable<K, V> c;

    public Memoizer3(Computable<K, V> c) {
        this.c = c;
    }
    @Override
    public V compute(K arg) throws InterruptedException {
        Future<V> future = cache.get(arg);
        if (future == null) {
            Callable<V> eval = new Callable<V>() {
                @Override
                public V call() throws Exception {
                    return c.compute(arg);
                }
            };
            FutureTask<V> futureTask = new FutureTask<V>(eval);
            future = futureTask;
            cache.put(arg,future);
            futureTask.run();
        }
        try {
            return future.get();
        } catch (ExecutionException e) {
            throw LaunderThrowable.launderThrowable(e.getCause());
        }
    }
}
